#!/bin/bash

# Copyright (C) 2016 Paul Kocialkowski <contact@paulk.fr>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

TOOL_ACTIONS_GENERIC="usage update execute"
TOOL_ACTIONS_HELPERS="arguments"
TOOL_FUNCTIONS=$( for action in $TOOL_ACTIONS_GENERIC ; do echo "$action" "$action""_check" ; done ; echo "$TOOL_ACTIONS_HELPERS" )

tool_include() {
	local tool=$1

	local tool_path=$( tool_path "$tool" )

	unset -f $TOOL_FUNCTIONS

	. "$tool_path/$tool"

	tool_helper_include "$tool"
}

tool_helper_include() {
	local tool=$1

	local tool_path=$( tool_path "$tool" )
	local include="$tool_path/$tool-helper"

	if [ -f "$include" ]
	then
		. "$include"
	fi
}

tool_check() {
	local tool=$1

	local tool_path=$( tool_path "$tool" )

	if ! [ -f "$tool_path/$tool" ]
	then
		return 1
	fi

	return 0
}

tool_function_check() {
	local tool=$1
	local function=$2

	tool_include "$tool"

	if ! function_check "$function"
	then
		return 1
	fi

	return 0
}

tool_action() {
	local action=$1
	shift
	local tool=$1
	shift
	local arguments=$@

	(
		set +e

		if ! tool_check "$tool"
		then
			printf "Tool $tool check failed\n" >&2
			return 1
		fi

		tool_action_check "$action" "$tool" "$@"

		if [ $? -eq 0 ]
		then
			return 0
		fi

		tool_include "$tool"

		if ! function_check "$action"
		then
			return 0
		fi

		printf "Tool $tool $action (with ${arguments:-no argument})\n" >&2

		(
			set -e
			"$action" "$@"
		)

		if [ $? -ne 0 ]
		then
			printf "\nTool $tool $action (with ${arguments:-no argument}) failed\n" >&2
			return 1
		else
			printf "\nTool $tool $action (with ${arguments:-no argument}) completed\n" >&2
		fi
	)
}

tool_action_check() {
	local action=$1
	shift
	local tool=$1
	shift

	(
		set +e

		if ! tool_check "$tool"
		then
			printf "Tool $tool check failed\n" >&2
			return 1
		fi

		tool_include "$tool"

		if ! function_check "$action""_check"
		then
			return 1
		fi

		for tool_force in $TOOLS_FORCE
		do
			if [ "$tool_force" = "$tool" ]
			then
				return 1
			fi
		done

		(
			set -e
			"$action""_check" "$@"
		)
	)
}

tool_action_helper() {
	local helper=$1
	shift
	local tool=$1
	shift

	(
		set +e

		if ! tool_check "$tool"
		then
			printf "Tool $tool check failed\n" >&2
			return 1
		fi

		tool_include "$tool"

		if ! function_check "$helper"
		then
			return 0
		fi

		(
			set -e
			"$helper" "$@"
		)
	)
}

tool_action_arguments_recursive() {
	local action=$1
	shift
	local tool=$1
	shift

	local action_helper_arguments=$( tool_action_helper "arguments" "$tool" "$@" )
	local argument

	if [ $? -ne 0 ] || [ -z "$action_helper_arguments" ]
	then
		tool_action "$action" "$tool" "$@"
	else
		echo "$action_helper_arguments" | while read argument
		do
			tool_action_arguments_recursive "$action" "$tool" "$@" "$argument"
		done
	fi
}

tool_path() {
	local tool=$1

	local tool_path="$root/$TOOLS/$tool"

	echo "$tool_path"
}

tool_sources_path() {
	local tool=$1
	shift

	local path="$root/$TOOLS/$tool/$SOURCES"
	local argument

	for argument in "" "$@"
	do
		if ! [ -z "$argument" ]
		then
			path="$path/$argument"
		fi

		if directory_filled_check "$path"
		then
			echo "$path"
			return
		fi
	done
}

tool_usage_actions() {
	local tool=$1
	shift

	printf "\nGeneric actions:\n"

	for action in $TOOL_ACTIONS_GENERIC
	do
		if function_check "$action"
		then
			printf "  $action\n"
		fi
	done

	if [ $# -gt 0 ]
	then
		printf "\nSpecific actions:\n"

		for action in "$@"
		do
			printf "  $action\n"
		done
	fi
}

tool_usage_arguments() {
	local tool=$1
	shift

	printf "\nArguments:\n"

	tool_usage_arguments_recursive "$tool" "  " "$@"
}

tool_usage_arguments_recursive() {
	local tool=$1
	shift
	local spacing=$1
	shift

	local action_helper_arguments=$( tool_action_helper "arguments" "$tool" "$@" )
	local argument

	if ! [ -z "$action_helper_arguments" ]
	then
		echo "$action_helper_arguments" | while read argument
		do
			printf "$spacing$argument\n"
			tool_usage_arguments_recursive "$tool" "  $spacing" "$@" "$argument"
		done
	fi
}

tool_file_path() {
	local tool=$1
	shift
	local directory=$1
	shift
	local file=$1
	shift

	local tool_path=$( tool_path "$tool" )
	local path="$tool_path/$directory"
	local argument
	local file_path

	for argument in "" "$@"
	do
		if ! [ -z "$argument" ]
		then
			path="$path/$argument"
		fi

		if ! [ -f "$path/$file" ]
		then
			continue
		fi

		file_path="$path/$file"
	done

	if [ -z "$file_path" ]
	then
		return 1
	fi

	echo "$file_path"
}

tool_file_test() {
	local file_path=$( tool_file_path "$@" )

	test -f "$file_path"
}

tool_file_contents() {
	local file_path=$( tool_file_path "$@" )

	if [ -f "$file_path" ]
	then
		cat "$file_path"
	fi
}

tool_file_contents_herit() {
	local tool=$1
	shift
	local directory=$1
	shift
	local file=$1
	shift

	local tool_path=$( tool_path "$tool" )
	local path="$tool_path/$directory"
	local argument
	local file_path

	for argument in "" "$@"
	do
		if ! [ -z "$argument" ]
		then
			path="$path/$argument"
		fi

		file_path="$path/$file"

		if ! [ -f "$file_path" ]
		then
			continue
		fi

		cat "$file_path"
	done
}
